今天是藍牙開發實作的最終篇,我們將完整展示這個專案的三個核心組成:MainViewController、BluetoothTableViewCell 以及 BluetoothService。這次的重點不在於新增功能,而是將所有元件整合起來,讓大家能清楚看到 XIB 與程式碼之間如何協同運作。
在 MainViewController 裡,我們主要負責管理藍牙設備的列表顯示、接收資料並即時更新介面。畫面中以 UITableView 呈現所有可偵測到的周邊設備,搭配 UILabel 顯示光線感測數據的即時變化,整體結構清晰又易於擴充。
而 BluetoothTableViewCell 則是自訂的表格元件,負責顯示每個藍牙設備的名稱,搭配簡潔的 XIB 佈局讓 UI 一目了然。
最後的 BluetoothService 是整個專案的靈魂。它透過 CoreBluetooth 框架進行藍牙掃描、連線、資料訂閱與數據回傳,並利用 delegate 機制與畫面進行資料交換。這樣的架構不僅模組化,後續若要擴充更多藍牙互動功能也相當方便。
這次展示完整的程式結構,讓你能從介面到邏輯一次掌握,了解一個具備即時資料顯示能力的藍牙應用是如何誕生的。

    //
    //  MainViewController.swift
    //  Bluetooth
    //
    //  Created by imac-2156 on 2025/8/15.
    //
    import UIKit
    import CoreBluetooth
    class MainViewController: UIViewController {
        
        // MARK: - IBOutlet(介面連接)
        
        @IBOutlet weak var tableView: UITableView!
        // 顯示藍牙設備列表的表格視圖
        @IBOutlet weak var lbLightNumber: UILabel!
        
        // 顯示接收數據的標籤
        
        // MARK: - Property(屬性)
        
        private var peripherals: [CBPeripheral] = []  // 儲存發現的藍牙設備
        private var connectedPeripheral: CBPeripheral? // 當前連接的設備
        
        // MARK: - LifeCycle(生命週期)
        
        override func viewDidLoad() {
            super.viewDidLoad()
            setUI()                                    // 設置介面
            BluetoothService.shared.delegate = self    // 設置藍牙服務代理
        }
        
        // MARK: - UI Settings(介面設置)
        
        func setUI() {
            tableView.delegate = self      // 設置表格視圖代理
            tableView.dataSource = self    // 設置表格視圖數據源
            
            // 註冊表格單元格
            tableView.register(UINib(nibName: "BluetoothTableViewCell", bundle: nil), forCellReuseIdentifier: "BluetoothTableViewCell")
            
            lbLightNumber.text = "等待數據..."  // 設置初始顯示文字
        }
        
        // MARK: - IBAction(介面操作)
        // (目前沒有按鈕操作)
        
        // MARK: - Function(自定義功能)
        // (目前沒有額外功能)
    }
    // MARK: - Extensions(擴展)
    // 藍牙服務代理實現
    extension MainViewController: BluetoothServiceDelegate {
        
        // 接收藍牙服務發現的設備列表
        func getBLEPeripherals(peripherals: [CBPeripheral]) {
            self.peripherals = peripherals  // 更新本地設備列表
            
            // 切換到主線程更新 UI
            DispatchQueue.main.async {
                self.tableView.reloadData()  // 重新載入表格數據
            }
        }
        
        // 接收從藍牙設備傳來的數據
        func getBLEPeripheralsValue(value: String) {
            // 切換到主線程更新 UI
            DispatchQueue.main.async {
                self.lbLightNumber.text = "光線強度:\(value)"  // 顯示接收的數據
            }
            
            print("\(value)")  // 在控制台打印數據
        }
    }
    // 表格視圖代理與數據源實現
    extension MainViewController: UITableViewDelegate, UITableViewDataSource {
        
        // 返回表格行數
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return peripherals.count  // 根據發現的設備數量決定行數
        }
        
        // 配置每一行的單元格
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            // 取得可重用的單元格並轉型為自定義單元格
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "BluetoothTableViewCell", for: indexPath) as? BluetoothTableViewCell else {
                return UITableViewCell()
            }
            
            let peripheral = peripherals[indexPath.row]  // 取得對應的設備
            
            // 設置單元格顯示的設備名稱
            cell.lbName.text = peripheral.name ?? "未知設備"
            
            return cell
        }
        
        // 處理用戶點擊某一行
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            tableView.deselectRow(at: indexPath, animated: true)  // 取消選中狀態
            
            let selectedPeripheral = peripherals[indexPath.row]  // 取得選中的設備
            
            // 呼叫藍牙服務連接該設備
            BluetoothService.shared.connectPeripheral(peripheral: selectedPeripheral)
        }
    }

//
//  BluetoothTableViewCell.swift
//  Bluetooth
//
//  Created by imac-2156 on 2025/8/15.
//
import UIKit
class BluetoothTableViewCell: UITableViewCell {
     
    @IBOutlet weak var lbName: UILabel!// 顯示設備名稱的標籤
    
    // 當從 Storyboard 或 Xib 文件載入時調用
    override func awakeFromNib() {
        super.awakeFromNib()
        // 在這裡進行初始化設置
    }
    // 當單元格被選中或取消選中時調用
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        // 在這裡配置選中狀態的視覺效果
    }
}
    //
    //  MainViewController.swift
    //  Bluetooth
    //
    //  Created by imac-2156 on 2025/8/15.
    //
    import UIKit
    import CoreBluetooth
    class MainViewController: UIViewController {
        
        // MARK: - IBOutlet(介面連接)
        
        @IBOutlet weak var tableView: UITableView!
        // 顯示藍牙設備列表的表格視圖
        @IBOutlet weak var lbLightNumber: UILabel!
        
        // 顯示接收數據的標籤
        
        // MARK: - Property(屬性)
        
        private var peripherals: [CBPeripheral] = []  // 儲存發現的藍牙設備
        private var connectedPeripheral: CBPeripheral? // 當前連接的設備
        
        // MARK: - LifeCycle(生命週期)
        
        override func viewDidLoad() {
            super.viewDidLoad()
            setUI()                                    // 設置介面
            BluetoothService.shared.delegate = self    // 設置藍牙服務代理
        }
        
        // MARK: - UI Settings(介面設置)
        
        func setUI() {
            tableView.delegate = self      // 設置表格視圖代理
            tableView.dataSource = self    // 設置表格視圖數據源
            
            // 註冊表格單元格
            tableView.register(UINib(nibName: "BluetoothTableViewCell", bundle: nil), forCellReuseIdentifier: "BluetoothTableViewCell")
            
            lbLightNumber.text = "等待數據..."  // 設置初始顯示文字
        }
        
        // MARK: - IBAction(介面操作)
        // (目前沒有按鈕操作)
        
        // MARK: - Function(自定義功能)
        // (目前沒有額外功能)
    }
    // MARK: - Extensions(擴展)
    // 藍牙服務代理實現
    extension MainViewController: BluetoothServiceDelegate {
        
        // 接收藍牙服務發現的設備列表
        func getBLEPeripherals(peripherals: [CBPeripheral]) {
            self.peripherals = peripherals  // 更新本地設備列表
            
            // 切換到主線程更新 UI
            DispatchQueue.main.async {
                self.tableView.reloadData()  // 重新載入表格數據
            }
        }
        
        // 接收從藍牙設備傳來的數據
        func getBLEPeripheralsValue(value: String) {
            // 切換到主線程更新 UI
            DispatchQueue.main.async {
                self.lbLightNumber.text = "光線強度:\(value)"  // 顯示接收的數據
            }
            
            print("\(value)")  // 在控制台打印數據
        }
    }
    // 表格視圖代理與數據源實現
    extension MainViewController: UITableViewDelegate, UITableViewDataSource {
        
        // 返回表格行數
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return peripherals.count  // 根據發現的設備數量決定行數
        }
        
        // 配置每一行的單元格
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            // 取得可重用的單元格並轉型為自定義單元格
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "BluetoothTableViewCell", for: indexPath) as? BluetoothTableViewCell else {
                return UITableViewCell()
            }
            
            let peripheral = peripherals[indexPath.row]  // 取得對應的設備
            
            // 設置單元格顯示的設備名稱
            cell.lbName.text = peripheral.name ?? "未知設備"
            
            return cell
        }
        
        // 處理用戶點擊某一行
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            tableView.deselectRow(at: indexPath, animated: true)  // 取消選中狀態
            
            let selectedPeripheral = peripherals[indexPath.row]  // 取得選中的設備
            
            // 呼叫藍牙服務連接該設備
            BluetoothService.shared.connectPeripheral(peripheral: selectedPeripheral)
        }
    }
)